/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mycompany.fb.visualization;

import static com.mycompany.fb.visualization.XMLGenerator.entriesSortedByValues;
import com.mycompany.fb.visualization.types.Friend;
import com.mycompany.fb.visualization.types.Town;
import com.restfb.Parameter;
import com.restfb.types.Conversation;
import com.restfb.types.Message;
import com.restfb.types.NamedFacebookType;
import com.restfb.types.User;
import infomanagers.BasicUserInfoManager;
import java.io.File;
import java.net.URL;
//import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
//import net.sf.saxon.dom.*;



/**
 *
 * @author Mato, Lenka
 */
public class XMLGenerator 
{
    private BasicUserInfoManager infoManager;
    
    
    static <K,V extends Comparable<? super V>> 
            List<Entry<K, V>> entriesSortedByValues(Map<K,V> map) 
    {

    List<Entry<K,V>> sortedEntries = new ArrayList<Entry<K,V>>(map.entrySet());

    Collections.sort(sortedEntries, 
            new Comparator<Entry<K,V>>() {
                @Override
                public int compare(Entry<K,V> e1, Entry<K,V> e2) {
                    return e2.getValue().compareTo(e1.getValue());
                }
            }
    );

    return sortedEntries;
    }
    
    /**
     * 
     * @param infoManager basic user info manager
     */
    public XMLGenerator(BasicUserInfoManager infoManager)
    {
        if(infoManager == null)
        {
            throw new IllegalArgumentException("infoManager is null");
        }
        this.infoManager = infoManager;
    }
    
    
   
    /**
     * 
     */
    public void generateXML()
    {
        try
        {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
        
        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("visualization");
        doc.appendChild(rootElement);
        
        Element towns = doc.createElement("towns");
        towns.setAttribute("count", Integer.toString(infoManager.getFriendsPlaces().size()));
        rootElement.appendChild(towns);
        
     
        List<Entry<Town, Long>> places = entriesSortedByValues(infoManager.getFriendsPlaces());
        for(Entry e : places)
        {
            Town place = (Town) e.getKey();
            Element town = doc.createElement("town");
            towns.appendChild(town);
            town.setAttribute("id", place.getId());
            town.setAttribute("name", (String) place.getCity());
            town.setAttribute("country", place.getCountry());
            town.setAttribute("latitude",  place.getLatitude().toString());
            town.setAttribute("longitude", place.getLongitude().toString());
            town.appendChild(doc.createTextNode(e.getValue().toString()));
        }
        
        /*for(String place : infoManager.getFriendsPlaces().keySet())
        {  
            Element town = doc.createElement("town");
            towns.appendChild(town);
            town.setAttribute("name", place);
            town.appendChild(doc.createTextNode(infoManager.getFriendsPlaces().get(place).toString()));
        }*/
        
        Element friends = doc.createElement("friends");
        rootElement.appendChild(friends);
        friends.setAttribute("count", Integer.toString(infoManager.getFriends().size()));    
        
        Map<Friend, Long> friendsWithCount = new HashMap<Friend, Long>();
        for(Friend u : infoManager.getFriends())
        {
            friendsWithCount.put(u, new Long(infoManager.getMutualFriends(u).size()));
        }

        
        List<Entry<Friend, Long>> sortedFriends = entriesSortedByValues(friendsWithCount);
        
        List<Friend> allShowedFriends = new ArrayList<Friend>();
        
        
        
        for(Entry u : sortedFriends/*infoManager.getFriends()*/)
        {
            Friend user = ((Friend) u.getKey());
            if(!allShowedFriends.contains(user))
            {
                List<Friend> mutualFriendsList = infoManager.getMutualFriends(user);

                String shortName = user.getFirstName().substring(0, 1) + ". " + user.getLastName();
                
                Element friend = doc.createElement("friend");
                friends.appendChild(friend);
                friend.setAttribute("id", user.getId());
                friend.setAttribute("gender", user.getGender());
                friend.setAttribute("birthday", user.getBirthday());
                friend.setAttribute("name", user.getName());
                friend.setAttribute("picture", "http://graph.facebook.com/" + user.getId()+ "/picture");
                friend.setAttribute("location", infoManager.getFriendsLocation(user));
                friend.setAttribute("short-name", shortName);
                
                Element mutualFriends = doc.createElement("mutual-friends");
                friend.appendChild(mutualFriends);
                mutualFriends.setAttribute("count", Integer.toString(mutualFriendsList.size()/*infoManager.getMutualFriends(user).size()*/));

                Map<Friend, Long> mutualFriendsWithCount = new HashMap<Friend, Long>();
                for(Friend us : mutualFriendsList/*infoManager.getMutualFriends(user)*/)
                {   
                    mutualFriendsWithCount.put(us, new Long(friendsWithCount.get(us)/*infoManager.getMutualFriends(us).size()*/));
                }
                List<Entry<Friend, Long>> sortedMutualFriends = entriesSortedByValues(mutualFriendsWithCount);



                for(Entry e : sortedMutualFriends/*infoManager.getMutualFriends(user)*/)
                {

                    Friend f = ((Friend) e.getKey());
                    if(!allShowedFriends.contains(f) && (double) infoManager.getMutualFriends(f).size() / sortedFriends.size() < 0.25)
                    {
                        /*if((double) infoManager.getMutualFriends(f).size() / sortedFriends.size() < 0.25)
                        {*/
                            String shortMutualName = f.getFirstName().substring(0, 1) + ". " + f.getLastName();
                            
                            Element mutualFriend = doc.createElement("mutual-friend");
                            mutualFriends.appendChild(mutualFriend);
                            mutualFriend.setAttribute("id", f.getId());
                            mutualFriend.setTextContent(f.getName());
                            mutualFriend.setAttribute("mutual-count", Integer.toString(infoManager.getMutualFriends(f).size()));
                            mutualFriend.setAttribute("gender", f.getGender());
                            mutualFriend.setAttribute("birthday", f.getBirthday());
                            mutualFriend.setAttribute("picture", "http://graph.facebook.com/" + f.getId()+ "/picture");
                            mutualFriend.setAttribute("location", infoManager.getFriendsLocation(f));
                            mutualFriend.setAttribute("short-name", shortMutualName);
                            
                            for(Friend fr : infoManager.getMutualFriends(f))
                            {
                                Element other = doc.createElement("other");
                                mutualFriend.appendChild(other);
                                other.setAttribute("id", fr.getId());
                            }
                            
                            allShowedFriends.add(f);
                        //}
                    }
                    else
                    {
                        Element other = doc.createElement("other");
                        mutualFriends.appendChild(other);
                        other.setAttribute("id", f.getId());
                    }
                }
                allShowedFriends.add(user);
            }
            
        }
                
        Map<Friend, Long> usersWithCount = new HashMap<Friend, Long>();
        Long totalMessages = 0l;
                
        for (Conversation c : infoManager.getConversations())
            {
                if(c.getParticipants().size() == 2)
                {
                totalMessages += c.getMessageCount();
                for(NamedFacebookType named: c.getParticipants())
                {
                    
                    Friend friend = new Friend(named.getId(), named.getName());
                    
                    if (!usersWithCount.keySet().contains(friend)) 
                    {
                         usersWithCount.put(friend, c.getMessageCount());
                         System.out.println(friend.getName() + ": " + c.getMessageCount());
                         if(c.getMessageCount() <= 25)
                         {
                             int i = 0;
                             for(Message m : c.getMessages())
                             {
                                 System.out.println(++i + ": " + m.getMessage());
                             }
                         }
                         
                    } 
                    else 
                    {
                         usersWithCount.put(friend, usersWithCount.get(friend) + c.getMessageCount());
                    }
                }
                }
            }
        usersWithCount.remove(new Friend(infoManager.getUserId(), infoManager.getUserName()));
        
        List<Entry<Friend, Long>> sortedUsersWithCount = entriesSortedByValues(usersWithCount);
        Element conversations = doc.createElement("conversations");
        conversations.setAttribute("total", totalMessages.toString());
        conversations.setAttribute("count", Integer.toString(sortedUsersWithCount.size()));
        rootElement.appendChild(conversations);
        
        for(Entry e : sortedUsersWithCount)
        {
            Friend f = (Friend) e.getKey();
            Element conversation = doc.createElement("conversation");
            conversation.setAttribute("id", f.getId());
            conversation.setAttribute("name", f.getName());
            conversation.appendChild(doc.createTextNode(e.getValue().toString()));
            conversations.appendChild(conversation);
        }
        
        /*for(User friendUser : infoManager.getFriends())
        {
            System.out.println(friendUser.getName() + "'s mutual friends(" + infoManager.getMutualFriends(friendUser).size()+"):");
            for(User u : infoManager.getMutualFriends(friendUser))
            {
                System.out.println(u.getName());
            }
        }*/
        
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        DOMSource source = new DOMSource(doc);
        
        StreamResult result = new StreamResult(new File("output.xml"));
        transformer.transform(source, result);
        String indexHtmlPath;
        URL url = this.getClass().getResource(""); 
        indexHtmlPath = url.getPath().split("WEB-INF/classes/com")[0] + "visualization.html";
        Transformation.simpleTransform("output.xml", "xmlProject.xsl", indexHtmlPath);
        
        System.out.println("XML generated");
        }
        catch (ParserConfigurationException e)
        {
            e.printStackTrace();
	} 
        catch (TransformerException e) 
        {
            e.printStackTrace();
	}
    }
}
